/*
 * Copyright 2013-2019 Xia Jun(3979434@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************************************
 *                                                                                     *
 *                        Website : http://www.farsunset.com                           *
 *                                                                                     *
 ***************************************************************************************
 */
package com.workcell.workdata.im.service.impl;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.workcell.workdata.im.component.event.FriendEvent;
import com.workcell.workdata.im.component.redis.UserRedisTemplate;
import com.workcell.workdata.im.constant.MessageAction;
import com.workcell.workdata.im.constant.UserState;
import com.workcell.workdata.im.entity.EventMessage;
import com.workcell.workdata.im.entity.Message;
import com.workcell.workdata.im.entity.User;
import com.workcell.workdata.im.model.vo.UserVO;
import com.workcell.workdata.im.repository.UserRepository;
import com.workcell.workdata.im.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {
	@Resource
	private UserRepository userRepository;

	@Resource
	private UserRedisTemplate userRedisTemplate;

	@Resource
	private ApplicationContext applicationContext;

	private final LoadingCache<Long, String> nameCache;

	@Autowired
	public UserServiceImpl(){
		nameCache = CacheBuilder.newBuilder()
				.maximumSize(1000)
				.expireAfterAccess(10, TimeUnit.MINUTES)
				.build(CacheLoader.from(id -> userRepository.findName(id)));
	}

	@Override
	public User findOne(Long id) {

		User user = userRedisTemplate.get(id);

		if (user != null) {
			return user;
		}

		User newUser = userRepository.findById(id).orElse(null);

		if (newUser != null){
			userRedisTemplate.save(newUser);
		}

		return newUser;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateName(Long id, String name) {

		userRepository.updateName(id,name);
		userRedisTemplate.remove(id);

		Message message = new EventMessage();
		message.setSender(id);
		message.setContent(name);
		message.setAction(MessageAction.ACTION_103);

		applicationContext.publishEvent(new FriendEvent(message));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateMotto(Long id, String motto) {
		userRepository.updateMotto(id,motto);
		userRedisTemplate.remove(id);

		Message message = new EventMessage();
		message.setSender(id);
		message.setContent(motto);
		message.setAction(MessageAction.ACTION_104);

		applicationContext.publishEvent(new FriendEvent(message));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updatePassword(Long id, String password) {
		userRepository.updatePassword(id,BCrypt.hashpw(password.toUpperCase(), BCrypt.gensalt(4)));
		userRedisTemplate.remove(id);
	}


	@Override
	public boolean checkPassword(Long id, String password) {
		return BCrypt.checkpw(password, userRepository.findPassword(id));
	}


	@Override
	public User findOne(String telephone) {
		return userRepository.findOne(telephone);
	}

	@Override
	public Long findId(String telephone) {
		return userRepository.findId(telephone);
	}

	@Override
	public String findName(long id) {
		return nameCache.getUnchecked(id);
	}

	@Override
	public Map<Long, String> findName(List<Long> idList) {
		return userRepository.findList(idList).stream().collect(Collectors.toMap(User::getId, User::getName));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void save(User user) {

		user.setCreateTime(new Date());

		user.setPassword(BCrypt.hashpw(user.getPassword().toUpperCase(), BCrypt.gensalt(4)));

		userRepository.saveAndFlush(user);

		userRedisTemplate.save(user);

	}

	@Override
	public boolean isExist(String telephone) {
		return userRepository.count(telephone) > 0;
	}

	@Override
	public Long getOrganizationId(Long uid) {
		return userRepository.getOrganizationId(uid);
	}

	@Override
	public List<User> findList(Collection<Long> idList) {
		return userRepository.findList(idList);
	}

	@Override
	public List<UserVO> findVoList(Collection<Long> idList) {
		return findList(idList).stream().map(UserVO::of).collect(Collectors.toList());
	}


	@Override
	public void destroy(long uid) {
		userRepository.updateState(uid, UserState.DESTROY.getValue());
	}

	@Override
	public User findbindKey(String bindKey) {
		return userRepository.findbindKey(bindKey);
	}

}
